#pragma once

#include <string>
#include <vector>
#include <exception>
#include <ctime>
#if defined (__linux__)
#   include <jsoncpp/json/json.h>
#else
#   include <json/json.h>
#endif
#include <easylog.h>

namespace cloud
{

class except : public std::exception
{
public:
    except(const std::string& msg) : s(msg)
    {}

    const char* what() const throw()
    {
        return s.c_str();
    }

    std::string s;
};

namespace util
{

struct file : easyfs::file
{
    file(const std::string& path) : easyfs::file(path) {}
};

struct dir : easyfs::directory
{
    dir(const std::string& path) : easyfs::directory(path) {}

    std::vector<std::string> scan()
    {
        std::vector<easyfs::path> paths = easyfs::directory::scan();
        std::vector<std::string> res;
        for (auto& path : paths)
            res.push_back(std::move(path.string()));
        return res;
    }

    std::vector<std::string> rscan()
    {
        std::vector<easyfs::path> paths = easyfs::directory::rscan();
        std::vector<std::string> res;
        for (auto& path : paths)
            res.push_back(path.string());
        return res;
    }
};

struct json
{
    json() = default;

    json(Json::Value root) : _root(root) {}

    void append(const util::json& js) { _root.append(js._root); }

    size_t size() const { return _root.size(); }

    json operator[](int i) const { return _root[i]; }

    template<typename value_type>
    json& set(const std::string& key, const value_type& value) { throw except("set not implemented for " + key); return *this; }

    template<typename value_type>
    value_type get(const std::string& key) const { throw except("get not implemented for " + key); }

    std::string serialize()
    {
        std::unique_ptr<Json::StreamWriter> sw(Json::StreamWriterBuilder().newStreamWriter());
        std::stringstream res;
        if (sw->write(_root, &res) != 0)
            throw except("serialize write error");
        return res.str();
    }

    json& deserialize(const std::string& str)
    {
        std::unique_ptr<Json::CharReader> cr(Json::CharReaderBuilder().newCharReader());

        std::string errs;
        if (!cr->parse(str.c_str(), str.c_str() + str.size(), &_root, &errs))
            throw except("deserialize parse error " + errs);
        return *this;
    }

private:
    void check(const std::string& key) const
    {
        if (!_root.isMember(key))
            throw except("has no value for " + key);
    }

    Json::Value _root;
};

    template<> json& json::set<bool>       (const std::string& key, const bool&        value) { _root[key] = value;           return *this; }
    template<> json& json::set<time_t>     (const std::string& key, const time_t&      value) { _root[key] = (int)value;      return *this; }
    template<> json& json::set<int>        (const std::string& key, const int&         value) { _root[key] = value;           return *this; }
    template<> json& json::set<size_t>     (const std::string& key, const size_t&      value) { _root[key] = (uint64_t)value; return *this; }
    template<> json& json::set<std::string>(const std::string& key, const std::string& value) { _root[key] = value;           return *this; }

    template<>
    bool json::get<bool>(const std::string& key) const
    {
        check(key);
        const Json::Value& val = _root[key];
        if (val.isConvertibleTo(Json::ValueType::booleanValue))
            return val.asBool();
        throw except("type mismatch for " + key);
    }

    template<>
    std::string json::get<std::string>(const std::string& key) const
    {
        check(key);
        const Json::Value& val = _root[key];
        if (val.isConvertibleTo(Json::ValueType::stringValue))
            return val.asString();
        throw except("type mismatch for " + key);
    }

    template<>
    time_t json::get<time_t>(const std::string& key) const
    {
        check(key);
        const Json::Value& val = _root[key];
        if (val.isConvertibleTo(Json::ValueType::uintValue))
            return val.asInt();
        throw except("type mismatch for " + key);
    }

    template<>
    int json::get<int>(const std::string& key) const
    {
        check(key);
        const Json::Value& val = _root[key];
        if (val.isConvertibleTo(Json::ValueType::intValue))
            return val.asUInt();
        throw except("type mismatch for " + key);
    }

    template<>
    size_t json::get<size_t>(const std::string& key) const
    {
        check(key);
        const Json::Value& val = _root[key];
        if (val.isConvertibleTo(Json::ValueType::uintValue))
            return val.asUInt();
        throw except("type mismatch for " + key);
    }

    template<>
    std::vector<std::string> json::get<std::vector<std::string>>(const std::string& key) const
    {
        check(key);
        const Json::Value& val = _root[key];
        std::vector<std::string> res;
        if (val.isConvertibleTo(Json::ValueType::arrayValue))
        {
            for (int i = 0; i < val.size(); i++)
                res.push_back(val[i].asString());
        }
        return res;
    }


struct killer
{
    static void kill_self()
    {
#if defined (__APPLE__) || defined (__linux__)
        raise(SIGKILL);
#elif defined (_WIN32) || defined (_WIN64)
        HANDLE handle = GetCurrentProcess();
        TerminateProcess(handle, 0);
        CloseHandle(handle);
#endif
    }
};

}
}
